package n50_atomic;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * @author fu
 * @date 2022-02-18 22:33
 * 概要：
 * ---------------------------------------------------------------
 * |                                                              |
 * |    LongAdder 比 AtomicLong 效率高很多                         |
 * |    LongAdder 设置多个累加单元，Therad-0 累加 Cell[0]，而 Thread-1 累加 Cell[1]... 最后将结果汇总
 * |              累加时操作的不同的 Cell 变量，因此减少了 CAS 重试失败，从而提高性能。
 * |                                                             |
 * |=============================================================
 */
public class T9_原子累加器_AtomicLong_And_LongAdder {

    public static void main(String[] args) {

        for (int i = 0; i < 5; i++) {
            demo(
                    ()->new AtomicLong(0),
//                x->x.getAndIncrement()
                    x->x.incrementAndGet()
            );
        }

        for (int i = 0; i < 5; i++) {
            demo(
                    ()->new LongAdder(),
                    x->x.increment()
            );
        }
/*
        打印：
        20000000 cost:337
        20000000 cost:383
        20000000 cost:342
        20000000 cost:338
        20000000 cost:336
        20000000 cost:70
        20000000 cost:35
        20000000 cost:34
        20000000 cost:49
        20000000 cost:35
            */
    }

    private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {
        T adder = adderSupplier.get();

        long start = System.nanoTime();


        List<Thread> ts = new ArrayList<>();

        // 4 个线程，每人累加 50 万
        for (int i = 0; i < 40; i++) {
            ts.add(new Thread(() -> {
                for (int j = 0; j < 500000; j++) {
                    action.accept(adder);
                }
            }));
        }
        ts.forEach(t -> t.start());
        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long end = System.nanoTime();
        System.out.println(adder + " cost:" + (end - start)/1000_000);
    }

}
